mod amf;
mod converter;

use std::{
    convert::TryInto,
    fmt,
    fs::File,
    io::{Read, Seek, Write},
};

use linked_hash_map::LinkedHashMap;
use log::{debug, error, trace};

use crate::{
    core::{
        amf::{Value, AMF},
        converter::ConvertBytes,
    },
    io::{DataReader, IOError},
};

pub const FLV_TAG_TYPE_AUDIO: u8 = 8;
pub const FLV_TAG_TYPE_VIDEO: u8 = 9;
pub const FLV_TAG_TYPE_SCRIPT: u8 = 18;
pub const FLV_SCRIPT_TYPE_NUMBER: u8 = 0;
pub const FLV_SCRIPT_TYPE_BOOLEAN: u8 = 1;
pub const FLV_SCRIPT_TYPE_STRING: u8 = 2;
pub const FLV_SCRIPT_TYPE_OBJECT: u8 = 3;
pub const FLV_SCRIPT_TYPE_MOVIE_CLIP: u8 = 4;
pub const FLV_SCRIPT_TYPE_NULL: u8 = 5;
pub const FLV_SCRIPT_TYPE_UNDEFINED: u8 = 6;
pub const FLV_SCRIPT_TYPE_REFERENCE: u8 = 7;
pub const FLV_SCRIPT_TYPE_ECMA_ARRAY: u8 = 8;
pub const FLV_SCRIPT_TYPE_SCRIPT_DATA_OBJECT_END: u8 = 9;
pub const FLV_SCRIPT_TYPE_STRICT_ARRAY: u8 = 10;
pub const FLV_SCRIPT_TYPE_DATE: u8 = 11;
pub const FLV_SCRIPT_TYPE_LONG_STRING: u8 = 12;

pub const FLV_SOUND_RATE_TABLE: [u32; 5] = [5500, 11025, 22050, 44100, 48000];
pub const MPEG_SAMPLING_RATES: [u32; 13] = [
    96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350,
];
pub const MPEG_AUDIO_V10_SAMPLE_RATE_TABLE: [u32; 4] = [44100, 48000, 32000, 0];
pub const MPEG_AUDIO_V20_SAMPLE_RATE_TABLE: [u32; 4] = [22050, 24000, 16000, 0];
pub const MPEG_AUDIO_V25_SAMPLE_RATE_TABLE: [u32; 4] = [11025, 12000, 8000, 0];
pub const MPEG_AUDIO_L1_BIT_RATE_TABLE: [i16; 16] = [
    0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1,
];
pub const MPEG_AUDIO_L2_BIT_RATE_TABLE: [i16; 14] = [
    0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, -1,
];
pub const MPEG_AUDIO_L3_BIT_RATE_TABLE: [i16; 12] =
    [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, -1];

pub const FLV_VIDEO_FRAME_TYPE_KEY_FRAME: u8 = 1;
pub const FLV_VIDEO_FRAME_TYPE_INTER_FRAME: u8 = 2;
pub const FLV_VIDEO_FRAME_TYPE_DISPOSABLE_INTER_FRAME: u8 = 3;
pub const FLV_VIDEO_FRAME_TYPE_GENERATED_KEY_FRAME: u8 = 4;
pub const FLV_VIDEO_FRAME_TYPE_VIDEO_INFO_OR_COMMAND_FRAME: u8 = 5;

pub const FLV_VIDEO_CODEC_ID_SORENSON_H263: u8 = 2;
pub const FLV_VIDEO_CODEC_ID_SCREEN_VIDEO: u8 = 3;
pub const FLV_VIDEO_CODEC_ID_ON2_VP6: u8 = 4;
pub const FLV_VIDEO_CODEC_ID_ON2_VP6_WITH_ALPHA_CHANNEL: u8 = 5;
pub const FLV_VIDEO_CODEC_ID_SCREEN_VIDEO_VERSION_2: u8 = 6;
pub const FLV_VIDEO_CODEC_ID_AVC: u8 = 7;

pub const FLV_AVC_PACKAGE_TYPE_AVC_SEQUENCE_HEADER: u8 = 0;
pub const FLV_AVC_PACKAGE_TYPE_AVC_NALU: u8 = 1;
pub const FLV_AVC_PACKAGE_TYPE_AVC_END_OF_SEQUENCE: u8 = 2;

pub const FLV_AUDIO_SOUND_FORMAT_LINEAR_PCM_PLATFORM_ENDIAN: u8 = 0;
pub const FLV_AUDIO_SOUND_FORMAT_ADPCM: u8 = 1;
pub const FLV_AUDIO_SOUND_FORMAT_MP3: u8 = 2;
pub const FLV_AUDIO_SOUND_FORMAT_LINEAR_PCM_LITTLE_ENDIAN: u8 = 3;
pub const FLV_AUDIO_SOUND_FORMAT_NELLYMOSER_16KHZ_MONO: u8 = 4;
pub const FLV_AUDIO_SOUND_FORMAT_NELLYMOSER_8KHZ_MONO: u8 = 5;
pub const FLV_AUDIO_SOUND_FORMAT_NELLYMOSER: u8 = 6;
pub const FLV_AUDIO_SOUND_FORMAT_G711_A_LAW_LOGARITHMIC_PCM: u8 = 7;
pub const FLV_AUDIO_SOUND_FORMAT_G711_MU_LAW_LOGARITHMIC_PCM: u8 = 8;
pub const FLV_AUDIO_SOUND_FORMAT_RESERVED: u8 = 9;
pub const FLV_AUDIO_SOUND_FORMAT_AAC: u8 = 10;
pub const FLV_AUDIO_SOUND_FORMAT_SPEEX: u8 = 11;
pub const FLV_AUDIO_SOUND_FORMAT_MP3_8KHZ: u8 = 14;
pub const FLV_AUDIO_SOUND_FORMAT_DEVICE_SPECIFIC_: u8 = 15;
// pub const FLV_AUDIO_SOUND_FORMAT_LINEAR_PCM: u8 = 14;
// pub const FLV_AUDIO_SOUND_FORMAT_LINEAR_PCM: u8 = 15;

pub const FLV_AUDIO_SOUND_RATE_5D5K: u8 = 0;
pub const FLV_AUDIO_SOUND_RATE_11K: u8 = 1;
pub const FLV_AUDIO_SOUND_RATE_22K: u8 = 2;
pub const FLV_AUDIO_SOUND_RATE_44K: u8 = 3;

pub const FLV_AUDIO_SOUND_SIZE_8B: u8 = 0;
pub const FLV_AUDIO_SOUND_SIZE_16B: u8 = 1;

pub const FLV_AUDIO_SOUND_TYPE_MONO: u8 = 0;
pub const FLV_AUDIO_SOUND_TYPE_STEREO: u8 = 1;

pub const FLV_AAC_PACKET_TYPE_AAC_SEQUENCE_HEADER: u8 = 0;
pub const FLV_AAC_PACKET_TYPE_AAC_RAW: u8 = 1;

#[derive(Debug)]
pub enum FLVError {
    ParseError(String),
    TypeError(Value, String),
    Complete,
    None,
    WriteError(String),
}

impl fmt::Display for FLVError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::ParseError(msg) => write!(f, "flv file error: {}", msg),
            Self::TypeError(v, msg) => write!(f, "type {} error: {}", v, msg),
            Self::Complete => write!(f, "Complete"),
            Self::None => write!(f, "None"),
            Self::WriteError(msg) => write!(f, "write flv file error {}", msg),
        }
    }
}

#[derive(Debug, Clone)]
pub struct TagHeader {
    pub tag_type: u8,
    pub data_size: u32,
    pub timestamp_base: u32,
    pub timestamp_extended: u32,
    pub stream_id: u32,
    pub data_addr: u64,
}

impl TagHeader {
    pub fn timestamp(&self) -> u32 {
        self.timestamp_extended << 24 | self.timestamp_base
    }
}

impl ConvertBytes for TagHeader {
    type T = u8;
    fn to_bytes(&self) -> Vec<Self::T> {
        let mut bytes = Vec::new();
        bytes.push(self.tag_type);
        bytes.append(&mut self.data_size.to_be_bytes()[1..].to_vec());
        bytes.append(&mut self.timestamp_base.to_be_bytes()[1..].to_vec());
        bytes.push(self.timestamp_extended as u8);
        bytes.append(&mut self.stream_id.to_be_bytes()[1..].to_vec());
        bytes
    }
}

#[derive(Debug, Clone)]
pub struct Keyframes {
    pub times: Vec<f64>,
    pub file_positions: Vec<f64>,
}

impl ConvertBytes for Keyframes {
    type T = u8;

    fn to_bytes(&self) -> Vec<Self::T> {
        let mut val = vec![0, 0x9];
        val.append(&mut "keyframes".as_bytes().to_vec());
        val.append(&mut vec![0x3, 0, 0xD]);
        val.append(&mut "filepositions".as_bytes().to_vec());
        val.push(FLV_SCRIPT_TYPE_STRICT_ARRAY);
        val.append(&mut (self.file_positions.len() as u32).to_be_bytes().to_vec());
        for fp in &self.file_positions {
            val.push(FLV_SCRIPT_TYPE_NUMBER);
            val.append(&mut fp.to_be_bytes().to_vec());
        }
        val.append(&mut vec![0, 0x5]);
        val.append(&mut "times".as_bytes().to_vec());
        val.push(FLV_SCRIPT_TYPE_STRICT_ARRAY);
        val.append(&mut (self.times.len() as u32).to_be_bytes().to_vec());
        for t in &self.times {
            val.push(FLV_SCRIPT_TYPE_NUMBER);
            val.append(&mut t.to_be_bytes().to_vec())
        }
        val
    }
}

#[derive(Clone, Debug)]
pub struct Metadata {
    pub data_addr: u64,
    pub keyframes: Option<Keyframes>,
    pub data: LinkedHashMap<String, Value>,
}

impl Metadata {
    pub fn new() -> Metadata {
        Metadata {
            data_addr: 0,
            data: LinkedHashMap::new(),
            keyframes: None,
        }
    }

    pub fn get(&self, key: &str) -> Option<&Value> {
        match self.data.get(&key.to_string()) {
            Some(v) => Some(v),
            None => None,
        }
    }

    pub fn set(&mut self, key: &str, value: Value) {
        self.data.insert(key.to_string(), value);
    }

    pub fn len(&self) -> usize {
        self.data.len()
    }

    pub fn get_f64(&self, key: &str) -> Result<&f64, FLVError> {
        match self.data.get(&key.to_string()) {
            Some(Value::Number(v)) => Ok(v),
            Some(v) => Err(FLVError::TypeError(
                v.clone(),
                format!("type must be Number").to_string(),
            )),
            None => Err(FLVError::None),
        }
    }

    pub fn get_bool(&self, key: &str) -> Result<&bool, FLVError> {
        match self.data.get(&key.to_string()) {
            Some(Value::Boolean(v)) => Ok(v),
            Some(v) => Err(FLVError::TypeError(
                v.clone(),
                format!("can not convert {} to bool", v),
            )),
            None => Err(FLVError::None),
        }
    }

    pub fn get_string(&self, key: &str) -> Result<&String, FLVError> {
        match self.data.get(&key.to_string()) {
            Some(Value::LongString(v)) | Some(Value::Strings(v)) => Ok(v),
            Some(v) => Err(FLVError::TypeError(v.clone(), format!(""))),
            None => Err(FLVError::None),
        }
    }

    pub fn get_vec(&self, key: &str) -> Result<&Vec<Value>, FLVError> {
        match self.data.get(&key.to_string()) {
            Some(Value::EcmaArray(v)) | Some(Value::StrictArray(v)) => Ok(v),
            Some(v) => Err(FLVError::TypeError(v.clone(), format!(""))),
            None => Err(FLVError::None),
        }
    }

    pub fn get_map(&self, key: &str) -> Result<&LinkedHashMap<String, Value>, FLVError> {
        match self.data.get(&key.to_string()) {
            Some(Value::Object(v)) => Ok(v),
            Some(v) => Err(FLVError::TypeError(v.clone(), format!(""))),
            None => Err(FLVError::None),
        }
    }
}

impl ConvertBytes for Metadata {
    type T = u8;
    fn to_bytes(&self) -> Vec<Self::T> {
        let mut bytes = vec![FLV_SCRIPT_TYPE_STRING, 0, 0xA];
        bytes.append(&mut String::from("onMetaData").into_bytes());
        bytes.push(FLV_SCRIPT_TYPE_ECMA_ARRAY);
        let count = match self.keyframes {
            Some(_) => self.len() + 1,
            None => self.len(),
        };
        bytes.append(&mut (count as u32).to_be_bytes().to_vec());
        for (k, v) in self.data.iter() {
            bytes.append(&mut (k.len() as u16).to_be_bytes().to_vec());
            bytes.append(&mut k.as_bytes().to_vec());
            bytes.append(&mut v.to_bytes());
        }

        if !matches!(self.keyframes, None) {
            bytes.append(&mut self.keyframes.as_ref().unwrap().to_bytes());
        }
        bytes.append(&mut vec![
            0,
            0,
            FLV_SCRIPT_TYPE_SCRIPT_DATA_OBJECT_END,
            0,
            0,
            FLV_SCRIPT_TYPE_SCRIPT_DATA_OBJECT_END,
        ]);
        bytes
    }
}

#[derive(Debug, Clone)]
pub struct SPS {
    pub sps_length: u16,
    pub sps_nal_unit: Vec<u8>,
}

impl ConvertBytes for SPS {
    type T = u8;
    fn to_bytes(&self) -> Vec<Self::T> {
        let mut val = self.sps_length.to_be_bytes().to_vec();
        val.append(&mut self.sps_nal_unit.clone());
        val
    }
}

/// Sequence Parameter Set
#[derive(Debug, Clone)]
pub struct SPSInfo {
    pub num_of_sps: u8,
    pub sps: Vec<SPS>,
}

impl SPSInfo {
    pub fn parse<R>(di: &mut DataReader<R>) -> (u8, SPSInfo)
    where
        R: Read + Seek,
    {
        let r2 = di.read_u8().unwrap();
        let reserved_2 = (r2 & 0b11100000) >> 5;
        let num_of_sps = r2 & 0b00011111;
        let mut sps = Vec::new();
        for _ in 0..num_of_sps {
            let sps_length = di.read_u16().unwrap();
            let sps_nal_unit = di.read(sps_length as u64).unwrap();
            sps.push(SPS {
                sps_length,
                sps_nal_unit,
            });
        }
        (reserved_2, SPSInfo { num_of_sps, sps })
    }

    pub fn length(&self) -> u64 {
        self.sps.iter().map(|p| p.sps_length as u64 + 2).sum()
    }
}

impl ConvertBytes for SPSInfo {
    type T = u8;
    fn to_bytes(&self) -> Vec<Self::T> {
        let mut val = vec![self.num_of_sps];
        for s in &self.sps {
            val.append(&mut s.to_bytes());
        }
        val
    }
}

#[derive(Debug, Clone)]
pub struct PPS {
    pub pps_length: u16,
    pub pps_nal_unit: Vec<u8>,
}

impl ConvertBytes for PPS {
    type T = u8;
    fn to_bytes(&self) -> Vec<Self::T> {
        let mut val = self.pps_length.to_be_bytes().to_vec();
        val.append(&mut self.pps_nal_unit.clone());
        val
    }
}

#[derive(Debug, Clone)]
pub struct PPSInfo {
    pub num_of_pps: u8,
    pub pps: Vec<PPS>,
}

impl PPSInfo {
    pub fn parse<R>(di: &mut DataReader<R>) -> PPSInfo
    where
        R: Read + Seek,
    {
        let num_of_pps = di.read_u8().unwrap();
        let mut pps = Vec::new();
        for _ in 0..num_of_pps {
            let pps_length = di.read_u16().unwrap();
            let pps_nal_unit = di.read(pps_length as u64).unwrap();
            pps.push(PPS {
                pps_length,
                pps_nal_unit,
            });
        }

        PPSInfo { num_of_pps, pps }
    }

    pub fn length(&self) -> u64 {
        self.pps.iter().map(|p| p.pps_length as u64 + 2).sum()
    }
}

impl ConvertBytes for PPSInfo {
    type T = u8;
    fn to_bytes(&self) -> Vec<Self::T> {
        let mut val = vec![self.num_of_pps];
        for p in &self.pps {
            val.append(&mut p.to_bytes());
        }
        val
    }
}

#[derive(Debug, Clone)]
pub struct AVCDecoderConfigurationRecord {
    pub configuration_version: u8,
    pub avc_profile_indication: u8,
    pub profile_compatibility: u8,
    pub avc_level_indication: u8,
    pub reserved_1: u8,
    pub length_size_minus_one: u8,
    pub reserved_2: u8,
    pub sps_info: SPSInfo,
    pub pps_info: PPSInfo,
    pub other_data: Vec<u8>,
}

impl AVCDecoderConfigurationRecord {
    pub fn parse<R>(di: &mut DataReader<R>, tag_header: &TagHeader) -> AVCDecoderConfigurationRecord
    where
        R: Read + Seek,
    {
        let configuration_version = di.read_u8().unwrap();
        let avc_profile_indication = di.read_u8().unwrap();
        let profile_compatibility = di.read_u8().unwrap();
        let avc_level_indication = di.read_u8().unwrap();
        let r1 = di.read_u8().unwrap();
        let reserved_1 = (r1 & 0b11111100) >> 2;
        let length_size_minus_one = r1 & 0b00000011;
        let (reserved_2, sps_info) = SPSInfo::parse(di);
        let pps_info = PPSInfo::parse(di);
        let other_data_len =
            tag_header.data_size as u64 - 12 - sps_info.length() - pps_info.length();
        let other_data = match other_data_len {
            l if l > 0 => di.read(l).unwrap(),
            _ => Vec::new(),
        };
        AVCDecoderConfigurationRecord {
            configuration_version,
            avc_profile_indication,
            profile_compatibility,
            avc_level_indication,
            reserved_1,
            length_size_minus_one,
            reserved_2,
            sps_info,
            pps_info,
            other_data,
        }
    }
}

impl ConvertBytes for AVCDecoderConfigurationRecord {
    type T = u8;
    fn to_bytes(&self) -> Vec<Self::T> {
        let mut val = vec![
            self.configuration_version,
            self.avc_profile_indication,
            self.profile_compatibility,
            self.avc_profile_indication,
            ((self.reserved_1 & 0b111111) << 2) | (self.length_size_minus_one & 0b11),
        ];
        let mut sps_bytes = self.sps_info.to_bytes();
        sps_bytes[0] = ((self.reserved_2 << 5) & 0b11100000) | (sps_bytes[0] & 0b00011111);
        val.append(&mut sps_bytes);
        val.append(&mut self.pps_info.to_bytes());
        val.append(&mut self.other_data.clone());

        val
    }
}

#[derive(Debug, Clone)]
pub struct AudioSpecificConfig {
    pub audio_object_type: u8,
    pub sampling_frequency_index: u8,
    pub channel_configuration: u8,
    pub other: u8,
    pub other_data: Vec<u8>,
}

impl ConvertBytes for AudioSpecificConfig {
    type T = u8;
    fn to_bytes(&self) -> Vec<Self::T> {
        let pre2byte = (self.audio_object_type as u16) << 11
            | (self.sampling_frequency_index as u16) << 7
            | (self.channel_configuration as u16) << 3
            | (self.other as u16);
        let mut val = pre2byte.to_be_bytes().to_vec();
        val.append(&mut self.other_data.clone());
        val
    }
}

#[derive(Debug, Clone)]
pub struct VideoData {
    pub data_addr: u64,
    pub frame_type: u8,
    pub codec_id: u8,
    pub avc_package_type: u8,
    pub composition_time_offset: u32,
    pub data: Vec<u8>,
    pub avc_decoder_configuration_record: Option<AVCDecoderConfigurationRecord>,
}

impl ConvertBytes for VideoData {
    type T = u8;
    fn to_bytes(&self) -> Vec<Self::T> {
        let first_byte = self.frame_type << 4 | self.codec_id;
        let mut val = vec![first_byte, self.avc_package_type];
        val.append(&mut self.composition_time_offset.to_be_bytes()[1..].to_vec());
        let mut body_data = match self.avc_package_type {
            FLV_AVC_PACKAGE_TYPE_AVC_SEQUENCE_HEADER => self
                .avc_decoder_configuration_record
                .as_ref()
                .unwrap()
                .to_bytes(),
            _ => self.data.clone(),
        };
        val.append(&mut body_data);
        val
    }
}

#[derive(Debug, Clone)]
pub struct AudioData {
    pub data_addr: u64,
    pub sound_format: u8,
    pub sound_rate: u8,
    pub sound_size: u8,
    pub sound_type: u8,
    pub aac_package_type: Option<u8>,
    pub audio_specific_config: Option<AudioSpecificConfig>,
    data: Vec<u8>,
}

impl ConvertBytes for AudioData {
    type T = u8;

    fn to_bytes(&self) -> Vec<Self::T> {
        let first_byte = ((self.sound_format << 4) & 0xF0)
            | ((self.sound_rate << 2) & 0xC)
            | ((self.sound_size << 1) & 0x2)
            | (self.sound_type & 0x1);
        let mut val = vec![first_byte];
        let mut body_data = match self.sound_format {
            FLV_AUDIO_SOUND_FORMAT_AAC => match self.aac_package_type {
                Some(FLV_AAC_PACKET_TYPE_AAC_SEQUENCE_HEADER) => {
                    let mut aac_body_data = vec![FLV_AAC_PACKET_TYPE_AAC_SEQUENCE_HEADER];
                    aac_body_data
                        .append(&mut self.audio_specific_config.as_ref().unwrap().to_bytes());
                    aac_body_data
                }
                Some(_) => {
                    let mut aac_body_data = vec![FLV_AAC_PACKET_TYPE_AAC_RAW];
                    aac_body_data.append(&mut self.data.clone());
                    aac_body_data
                }
                None => Vec::new(),
            },
            _ => self.data.clone(),
        };
        val.append(&mut body_data);
        val
    }
}

#[derive(Debug)]
pub struct Tag {
    pub data_addr: u64,
    pub previous_tag_size: u32,
    pub header: TagHeader,
    pub video_data: Option<VideoData>,
    pub audio_data: Option<AudioData>,
    pub metadata: Option<Metadata>,
}

impl Tag {
    pub fn is_metadata(&self) -> bool {
        !matches!(self.metadata, None)
    }

    pub fn is_avc_decoder_config(&self) -> bool {
        if self.header.tag_type != FLV_TAG_TYPE_VIDEO {
            return false;
        }
        match &self.video_data {
            Some(data) => data.avc_package_type == FLV_AVC_PACKAGE_TYPE_AVC_SEQUENCE_HEADER,
            None => false,
        }
    }

    pub fn is_aac_spec_config(&self) -> bool {
        if self.header.tag_type != FLV_TAG_TYPE_AUDIO {
            return false;
        }
        match &self.audio_data {
            Some(data) => match data.aac_package_type {
                None => false,
                Some(aac_package_type) => {
                    aac_package_type == FLV_AAC_PACKET_TYPE_AAC_SEQUENCE_HEADER
                }
            },
            None => false,
        }
    }

    pub fn size(&self) -> u32 {
        self.header.data_size + 11
    }

    pub fn parse<R>(di: &mut DataReader<R>) -> Result<Tag, FLVError>
    where
        R: Read + Seek,
    {
        let header_addr = di.offset();
        let previous_tag_size = di.read_u32().unwrap();
        trace!("previous_tag_size: {}", previous_tag_size);
        let tag_type_result = di.read_u8();
        if tag_type_result.is_err() {
            match tag_type_result.unwrap_err() {
                IOError::ReadCompleted(_, _) => return Err(FLVError::Complete),
                _ => return Err(FLVError::ParseError("parse flv error".to_string())),
            }
        }

        let tag_type = tag_type_result.unwrap();
        let data_size = di.read_to_u64(3).unwrap() as u32;
        let timestamp_base = di.read_to_u64(3).unwrap() as u32;
        let timestamp_extended = di.read_to_u64(1).unwrap() as u32;
        let stream_id = di.read_to_u64(3).unwrap() as u32;
        let tag_header = TagHeader {
            data_addr: header_addr,
            tag_type,
            data_size,
            timestamp_base,
            timestamp_extended,
            stream_id,
        };
        trace!("parse header: {:?}", tag_header);
        match tag_type {
            FLV_TAG_TYPE_SCRIPT => Ok(Tag {
                data_addr: di.offset(),
                header: tag_header,
                metadata: Some(Tag::parse_script(di)),
                video_data: None,
                audio_data: None,
                previous_tag_size,
            }),
            FLV_TAG_TYPE_AUDIO => {
                let audio_data = Tag::parse_audio(di, &tag_header);
                Ok(Tag {
                    data_addr: di.offset(),
                    header: tag_header,
                    metadata: None,
                    video_data: None,
                    audio_data: Some(audio_data),
                    previous_tag_size,
                })
            }
            FLV_TAG_TYPE_VIDEO => {
                let video_data = Tag::parse_video(di, &tag_header);
                Ok(Tag {
                    data_addr: di.offset(),
                    header: tag_header,
                    metadata: None,
                    video_data: Some(video_data),
                    audio_data: None,
                    previous_tag_size,
                })
            }
            _ => Err(FLVError::ParseError(
                format!("FLV tag type is {}, must be 8, 9, 18", tag_type).to_string(),
            )),
        }
    }

    fn parse_script<R>(di: &mut DataReader<R>) -> Metadata
    where
        R: Read + Seek,
    {
        let data_addr = di.offset();
        let name_data_type = di.read_u8().unwrap();
        if name_data_type != FLV_SCRIPT_TYPE_STRING {
            error!("Script Tag AMF1 type error {}", name_data_type);
            panic!("Script Tag AMF1 type error {}", name_data_type);
        }
        let name_data_size = di.read_u16().unwrap();
        let name = di.read_string(name_data_size as u64).unwrap();
        if !"onMetaData".eq_ignore_ascii_case(&name) {
            panic!("Script Tag AMF1 string error: {}", name);
        }
        let data_type = di.read_u8().unwrap();
        if data_type != FLV_SCRIPT_TYPE_ECMA_ARRAY {
            panic!("Script Tag AMF2 type error: {}", data_type);
        }
        let count = di.read_u32().unwrap();
        let mut data: LinkedHashMap<String, Value> = LinkedHashMap::new();
        let mut keyframes: Option<Keyframes> = None;
        for _ in 0..count {
            match AMF::parse_variable(di) {
                Ok((k, v)) => {
                    if k.eq("keyframes") {
                        keyframes = match v {
                            Value::Object(v) => {
                                let times: Vec<f64> = v.get("times").unwrap().try_into().unwrap();
                                let file_positions: Vec<f64> =
                                    v.get("filepositions").unwrap().try_into().unwrap();
                                Some(Keyframes {
                                    times,
                                    file_positions,
                                })
                            }
                            _ => None,
                        }
                    } else {
                        data.insert(k, v);
                    }
                }
                Err(_) => continue,
            }
        }
        di.skip(3);
        Metadata {
            data_addr,
            keyframes,
            data,
        }
    }

    fn parse_audio<R>(di: &mut DataReader<R>, tag_header: &TagHeader) -> AudioData
    where
        R: Read + Seek,
    {
        let data_addr = di.offset();
        let sound_info = di.read_u8().unwrap();
        let sound_format = (sound_info & 0b11110000) >> 4;
        let sound_rate = (sound_info & 0b00001100) >> 2;
        let sound_size = (sound_info & 0b00000010) >> 1;
        let sound_type = sound_info & 0b00000001;
        let mut aac_package_type: Option<u8> = None;
        let (audio_spec_config, data): (Option<AudioSpecificConfig>, Vec<u8>) = match sound_format {
            FLV_AUDIO_SOUND_FORMAT_AAC => {
                aac_package_type = Some(di.read_u8().unwrap());
                match aac_package_type {
                    Some(FLV_AAC_PACKET_TYPE_AAC_SEQUENCE_HEADER) => {
                        let val = di.read_u16().unwrap();
                        let audio_object_type = ((val & 0b1111100000000000) >> 11) as u8;
                        let sampling_frequency_index = ((val & 0b0000011110000000) >> 7) as u8;
                        let channel_configuration = ((val & 0b0000000001111000) >> 3) as u8;
                        let other = (val & 0b0000000000000111) as u8;
                        let other_data = di.read((tag_header.data_size - 4) as u64).unwrap();
                        (
                            Some(AudioSpecificConfig {
                                audio_object_type,
                                sampling_frequency_index,
                                channel_configuration,
                                other,
                                other_data,
                            }),
                            Vec::new(),
                        )
                    }
                    Some(FLV_AAC_PACKET_TYPE_AAC_RAW) => {
                        (None, di.read((tag_header.data_size - 2) as u64).unwrap())
                    }
                    Some(_) => (None, Vec::new()),
                    None => (None, Vec::new()),
                }
            }
            FLV_AUDIO_SOUND_FORMAT_MP3 => {
                (None, di.read((tag_header.data_size - 1) as u64).unwrap())
            }
            t => panic!("{} not support", t),
        };
        AudioData {
            data_addr,
            sound_format,
            sound_rate,
            sound_size,
            sound_type,
            aac_package_type,
            audio_specific_config: audio_spec_config,
            data,
        }
    }

    fn parse_video<R>(di: &mut DataReader<R>, tag_header: &TagHeader) -> VideoData
    where
        R: Read + Seek,
    {
        let data_addr = di.offset();
        let val = di.read_u8().unwrap();
        let frame_type = (val & 0xf0) >> 4;
        let codec_id = val & 0x0f;
        let avc_package_type = di.read_u8().unwrap();
        let composition_time_offset = di.read_to_u64(3).unwrap() as u32;
        let (avc_decoder_configuration_record, data) = match avc_package_type {
            FLV_AVC_PACKAGE_TYPE_AVC_SEQUENCE_HEADER => (
                Some(AVCDecoderConfigurationRecord::parse(di, tag_header)),
                Vec::new(),
            ),
            _ => {
                let limit = tag_header.data_size - 5;
                let data: Vec<u8>;
                if limit <= 0 {
                    data = Vec::new();
                } else {
                    data = di.read(limit.into()).unwrap();
                }
                (None, data)
            }
        };

        VideoData {
            data_addr,
            frame_type,
            codec_id,
            avc_package_type,
            composition_time_offset,
            avc_decoder_configuration_record,
            data,
        }
    }
}

impl ConvertBytes for Tag {
    type T = u8;

    fn to_bytes(&self) -> Vec<Self::T> {
        let mut val = Vec::new();
        val.append(&mut self.previous_tag_size.to_be_bytes().to_vec());
        val.append(&mut self.header.to_bytes());
        val.append(
            &mut (match self.header.tag_type {
                FLV_TAG_TYPE_SCRIPT => self.metadata.as_ref().unwrap().to_bytes(),
                FLV_TAG_TYPE_VIDEO => self.video_data.as_ref().unwrap().to_bytes(),
                FLV_TAG_TYPE_AUDIO => self.audio_data.as_ref().unwrap().to_bytes(),
                _ => panic!("tag type {} not supported", self.header.tag_type),
            }),
        );
        val
    }
}

#[derive(Debug)]
pub struct Header {
    pub signature: String,
    pub version: u8,
    pub type_flags_audio: bool,
    pub type_flags_video: bool,
    pub size: u32,
}

impl Header {
    pub fn parse<R>(di: &mut DataReader<R>) -> Header
    where
        R: Read + Seek,
    {
        let data = di.read(9).unwrap();
        Header {
            signature: String::from_utf8(data[..3].to_vec()).unwrap(),
            version: data[3],
            type_flags_audio: (data[4] & 0b00000100) >> 2 == 1,
            type_flags_video: (data[4] & 0b00000001) == 1,
            size: u32::from_be_bytes(data[5..9].to_vec().try_into().unwrap()),
        }
    }

    pub fn has_video(&self) -> bool {
        self.type_flags_video
    }
    pub fn has_audio(&self) -> bool {
        self.type_flags_audio
    }
    pub fn to_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::new();
        let mut signature_bytes = self.signature.clone().into_bytes();
        bytes.append(&mut signature_bytes);
        bytes.push(self.version);
        let mut type_flags = 0u8;
        type_flags = match self.type_flags_audio {
            true => type_flags | 0b00000100,
            false => type_flags | 0,
        };
        type_flags = match self.type_flags_video {
            true => type_flags | 0b00000101,
            false => type_flags | 0b00000100,
        };
        bytes.push(type_flags);
        bytes.append(&mut self.size.to_be_bytes().to_vec());

        bytes
    }
}

#[derive(Debug)]
pub struct Body {
    metadata_idx: usize,
    avc_decoder_config_idx: usize,
    aac_spec_config_idx: Option<usize>,
    pub tags: Vec<Tag>,
}

impl Body {
    pub fn parse<R>(di: &mut DataReader<R>) -> Body
    where
        R: Read + Seek,
    {
        let mut tags: Vec<Tag> = Vec::new();
        let mut idx = 0usize;
        let mut metadata_idx = 0usize;
        let mut avc_decoder_config_idx = 0usize;
        let mut aac_spec_config_idx = None;
        loop {
            let di_ = &mut *di;
            let tag_result = Tag::parse(di_);
            match tag_result {
                Ok(tag) => {
                    if tag.is_metadata() {
                        metadata_idx = idx;
                    } else if tag.is_avc_decoder_config() {
                        avc_decoder_config_idx = idx;
                    } else if tag.is_aac_spec_config() {
                        aac_spec_config_idx = Some(idx);
                    }
                    tags.push(tag);
                }
                Err(FLVError::Complete) => break,
                Err(e) => {
                    error!("parse body error: {}", e);
                    panic!("{:?}", e)
                }
            }
            idx += 1;
        }
        Body {
            metadata_idx,
            avc_decoder_config_idx,
            aac_spec_config_idx,
            tags,
        }
    }

    pub fn metadata(&self) -> &Tag {
        &self.tags[self.metadata_idx]
    }

    pub fn avc_decoder_config(&self) -> &Tag {
        &self.tags[self.avc_decoder_config_idx]
    }

    pub fn aac_spec_config(&self) -> Option<&Tag> {
        match self.aac_spec_config_idx {
            Some(idx) => Some(&self.tags[idx]),
            None => None,
        }
    }
}

#[derive(Debug)]
pub struct FLV {
    pub header: Header,
    pub body: Body,
}

impl FLV {
    pub fn open(file_path: &str) -> Result<FLV, FLVError> {
        debug!("start read file：{}", file_path);
        let file = File::open(file_path);
        if file.is_err() {
            return Err(FLVError::ParseError(format!("{}", file.unwrap_err())));
        }
        let mut data_reader = DataReader::open(file.unwrap());
        let header = Header::parse(&mut data_reader);
        trace!("parse header: {:?}", header);
        let body = Body::parse(&mut data_reader);
        debug!("end read file：{}", file_path);
        Ok(FLV { header, body })
    }

    pub fn open_with_reader<R: Read + Seek>(reader: &mut R) -> Result<FLV, FLVError> {
        let mut data_reader = DataReader::open(reader);
        let header = Header::parse(&mut data_reader);
        debug!("parse header: {:?}", header);
        let body = Body::parse(&mut data_reader);
        Ok(FLV { header, body })
    }

    pub fn write_file(&mut self, file_path: &str) -> Result<u8, FLVError> {
        let mut file = File::create(file_path).unwrap();
        self.write(&mut file)
    }

    pub fn write<F: Write + Seek>(&mut self, f: &mut F) -> Result<u8, FLVError> {
        f.write(self.header.to_bytes().as_slice()).unwrap();
        for tag in &self.body.tags {
            f.write(tag.to_bytes().as_slice()).unwrap();
        }
        let last_tag = &self.body.tags[self.body.tags.len() - 1];
        f.write(&last_tag.size().to_be_bytes().to_vec()).unwrap();
        Ok(0)
    }
}
